#include "cclient.h"
#include "crootwindow.h"

#include <string.h>

#include <buola/algorithm/container.h>
#include <buola/gui/cicon.h>
#include <buola/gui/x11/cx11window.h>
#include <buola/image/cimage.h>
#include <buola/gui/x11/cx11display.h>
#include <buola/gui/x11/cx11toolkit.h>
#include <buola/gui/x11/cpixmap.h>
#include <buola/gui/x11/icccm.h>

#include <X11/Xatom.h>

namespace buola { namespace bdesktop {

CClient::CClient()
    :   mWindow(nullptr)
{
}

CClient::~CClient()
{
}

bool CClient::Create(Window pID)
{
    mWindow=new gui::x11::CX11Window(nullptr,gui::x11::get_default_x11_display());
    mWindow->CreateForeign(pID,gui::EVENT_NONE);

    if(!mWindow->GetID())
    {
        delete mWindow;
        mWindow=nullptr;
        return false;
    }

    mTBFlags=TBF_NONE;
    mTBType=gui::CWindow::CLASS_NORMAL;

    if(!mWindow->GetWMProperty(gui::x11::get_default_x11_display()->Atoms().mNetWMVisibleName,mName,gui::x11::get_default_x11_display()->Atoms().mUTF8))
        mWindow->GetWMProperty(gui::x11::get_default_x11_display()->Atoms().mName,mName);
    if(!mWindow->GetWMProperty(gui::x11::get_default_x11_display()->Atoms().mNetWMVisibleIconName,mIconName,gui::x11::get_default_x11_display()->Atoms().mUTF8))
        mWindow->GetWMProperty(gui::x11::get_default_x11_display()->Atoms().mIconName,mIconName);

    XClassHint *lClassHint=XAllocClassHint();
    XGetClassHint(gui::x11::get_default_x11_display()->GetDisplay(),pID,lClassHint);

    mApplicationName=lClassHint->res_name?:"";
    mApplicationClass=lClassHint->res_class?:"";

    if(lClassHint->res_name)
        XFree(lClassHint->res_name);
    if(lClassHint->res_class&&lClassHint->res_name!=lClassHint->res_class)
        XFree(lClassHint->res_class);

    XFree(lClassHint);

    GetClientGeometry();
    gui::x11::icccm::read_net_wm_desktop(mWindow->GetID(),mTBDesktop);
    mTBType=gui::x11::icccm::read_net_wm_window_type(mWindow->GetID());
    gui::x11::icccm::read_net_wm_icon(mWindow->GetID(),mTBIcons);
    ReadState();
    
    return true;
}

bool CClient::ShouldBeInTaskBar()
{
    if(GetTBFlag(TBF_WITHDRAWN)||GetTBFlag(TBF_SKIPTASKBAR)||
            mTBType==gui::CWindow::CLASS_DESKTOP||mTBType==gui::CWindow::CLASS_DOCK)
    {
        return false;
    }
    else
    {
        return true;
    }
}

bool CClient::ShouldBeInPager()
{
    if(GetTBFlag(TBF_HIDDEN)||GetTBFlag(TBF_SKIPPAGER)||
        GetTBFlag(TBF_WITHDRAWN)||mTBType==gui::CWindow::CLASS_DESKTOP||mTBType==gui::CWindow::CLASS_DOCK)
    {
        return false;
    }
    else
    {
        return true;
    }
}

void CClient::WMActivate()
{
    mWindow->SendClientMessage(gRootWindow,gui::x11::get_default_x11_display()->Atoms().mNetActiveWindow,0);
}

void CClient::WMSendMessage(Atom pAtom,long pParam)
{
    XEvent lE;

    memset(&lE, 0, sizeof(lE));
    lE.xclient.type = ClientMessage;
    lE.xclient.window = mWindow->GetID();
    lE.xclient.message_type = pAtom;
    lE.xclient.format = 32;
    lE.xclient.data.l[0] = pParam;
    lE.xclient.data.l[1] = CurrentTime;

    if(XSendEvent(gui::x11::get_default_x11_display()->GetDisplay(), mWindow->GetID(), false, 0, &lE)==0)
        msg_warn() << "CClientWindow::sendMessage failed\n";

}

/////////////////////////////////////////////////////////
///////////////// accessing properties //////////////////
/////////////////////////////////////////////////////////

PImage CClient::GetTBIcon(const CSize &pSize)
{
    if(!mTBIcons.size()&&!mTBIcon) return nullptr;

    int lBestSizeIndex=-1;
    CSize lBestSize=CSize(0,0);

    for(int i=0;i<mTBIcons.size();i++)
    {
        CSize lSize=mTBIcons[i]->GetSize();
        if(lSize==pSize)
            return mTBIcons[i];

        if(lBestSize<pSize) //nothing really good found
        {
            if(lSize>lBestSize)
            {
                lBestSizeIndex=i;
                lBestSize=lSize;
            }
        }
        else
        {
            if(lSize>=pSize&&lSize<lBestSize)
            {
                lBestSizeIndex=i;
                lBestSize=lSize;
            }
        }
    }

    if(mTBIcon)
    {
        PImage lTBIcon=mTBIcon->GetImage((int)pSize.x);
        mTBIcons.push_back(lTBIcon);
        return lTBIcon;
    }
    else if(mTBIcons.size())
    {
        if(lBestSizeIndex<0) return nullptr;

        PImage lTBIcon=new CImage;
        mTBIcons.push_back(lTBIcon);
        lTBIcon->Create(mTBIcons[lBestSizeIndex]);
        lTBIcon->Scale(CIntSize(pSize));
        return lTBIcon;
    }
    else
    {
        mTBIcon=gui::CIcon::Get("view-close");
        PImage lTBIcon=mTBIcon->GetImage((int)pSize.x);
        mTBIcons.push_back(lTBIcon);
        return lTBIcon;
    }
}

void CClient::GetClientGeometry()
{
    Window lD1;
    int lD2,lD3;
    int lX,lY;
    unsigned int lW,lH;
    unsigned int lD4,lD5;

    XGetGeometry(gui::x11::get_default_x11_display()->GetDisplay(),mWindow->GetID(),&lD1,&lD2,&lD3,
                &lW,&lH,&lD4,&lD5);
    //the window manager might have reparented the window, so we can't use the position returned by XGetGeometry
    XTranslateCoordinates(gui::x11::get_default_x11_display()->GetDisplay(),mWindow->GetID(),
                gRootWindow->GetID(),0,0,&lX,&lY,&lD1);
    mTBPos.Set(lX,lY);
    mTBSize.Set(lW,lH);
}

void CClient::ReadState()
{
    if(gui::x11::icccm::read_wm_state(mWindow->GetID())==gui::x11::icccm::WMSTATE_WITHDRAWN)
        SetTBFlag(TBF_WITHDRAWN);
    else
        UnsetTBFlag(TBF_WITHDRAWN);

    gui::x11::icccm::ENetState lState=gui::x11::icccm::read_net_wm_state(mWindow->GetID());
    UnsetTBFlag(TBF_SKIPTASKBAR);
    UnsetTBFlag(TBF_HIDDEN);
    
    if(lState&gui::x11::icccm::NETSTATE_SKIPTASKBAR)
        SetTBFlag(TBF_SKIPTASKBAR);
    else if(lState&gui::x11::icccm::NETSTATE_HIDDEN)
        SetTBFlag(TBF_HIDDEN);
    
    gRootWindow->sClientChange(this);
}

bool CClient::OnX11Event(XEvent *pE)
{
    switch (pE->xany.type)
    {
    case PropertyNotify:
        DoProperty(&pE->xproperty);
        return false;
    }

    return true;
}

void CClient::DoProperty(XPropertyEvent *pE)
{
    Atom lAtom = pE->atom;

    if(lAtom==gui::x11::get_default_x11_display()->Atoms().mNetWMState)
    {
        ReadState();
    }
    else if(lAtom==gui::x11::get_default_x11_display()->Atoms().mNetWMVisibleName)
    {
        if(!mWindow->GetWMProperty(gui::x11::get_default_x11_display()->Atoms().mNetWMVisibleName,mName,gui::x11::get_default_x11_display()->Atoms().mUTF8))
            mWindow->GetWMProperty(gui::x11::get_default_x11_display()->Atoms().mName,mName);

        gRootWindow->sClientChange(this);
    }
    else if(lAtom==gui::x11::get_default_x11_display()->Atoms().mState)
    {
        ReadState();
    }
    else if(lAtom==gui::x11::get_default_x11_display()->Atoms().mNetWMDesktop)
    {
        gui::x11::icccm::read_net_wm_desktop(mWindow->GetID(),mTBDesktop);
        
        gRootWindow->sDesktopChange();
    }
    else if(lAtom==gui::x11::get_default_x11_display()->Atoms().mNetWMIcon)
    {
        mTBIcon=nullptr;
        gui::x11::icccm::read_net_wm_icon(mWindow->GetID(),mTBIcons);

        gRootWindow->sClientChange(this);
    }
    else if(lAtom==XA_WM_NAME||lAtom==XA_WM_ICON_NAME)
    {
    }
    else if(lAtom==XA_WM_CLIENT_MACHINE||lAtom==gui::x11::get_default_x11_display()->Atoms().mLocaleName)
    {
    }
    else
    {
        msg_debug<5>() << "Changed client property " <<
            gui::x11::get_default_x11_display()->GetAtomName(lAtom) << "\n";
    }
}

void CClient::OnMove(gui::CGeometryEvent *pE)
{
    GetClientGeometry();
    gRootWindow->sClientGeometry(this);
}

void CClient::OnSize(gui::CGeometryEvent *pE)
{
    GetClientGeometry();
    gRootWindow->sClientGeometry(this);
}

/*namespace bdesktop*/ } /*namespace buola*/ }
